//
//  UserCenter.swift
//  Center
//
//  Created by 蒋晨成 on 2021/12/29.
//

import Center_RPC
import Center_Storage
import Foundation
import GRPC
import LogMan
import Standard

public enum UserCenter {}

extension UserCenter: TypeName {}

public typealias LogoutBlock = () -> Void

private extension UserCenter {
    static let store = StorageCenter.azir[typeName]
    static var logoutHandlers: [UserCenterLogoutProtocol.Type] = []
    static var profileChangeHandlers: [WeakProfileChangeHandler] = []
}

extension UserCenter {
    static let logger = Loggers[typeName]
}

public extension UserCenter {
    enum Sign {}
}

// MARK: - Handle User Logout

extension UserCenter {
    static let logoutLock = Lock()

    // 若需要在用户登出后执行清理操作，需要实现 UserCenterLogoutProtocol 协议，并调用此方法
    @discardableResult
    public static func appendLogoutHandler(handler: UserCenterLogoutProtocol.Type) -> UserCenter.Type {
        logoutHandlers.append(handler)
        return self
    }

    static func clearLogoutHandlers() {
        logoutHandlers = []
    }

    static func callLogoutHandlers() {
        logoutHandlers.forEach { handler in
            handler.userDidLogout()
        }
    }

    static func logoutAndClear() {
        logoutLock.lock()
        defer { logoutLock.unlock() }
        guard UserCenter.isLogin else {
            return
        }
        callLogoutHandlers()
        clearStorage()
    }
}

// MARK: - Handle User ProfileChange

public extension UserCenter {
    class WeakProfileChangeHandler {
        weak var handler: UserCenterProfileChangeProtocol?

        init(_ handler: UserCenterProfileChangeProtocol? = nil) {
            self.handler = handler
        }
    }

    // 若需要在用户登出后执行清理操作，需要实现 UserCenterLogoutProtocol 协议，并调用此方法
    static func appendProfileChangeHandler(handler: UserCenterProfileChangeProtocol) {
        profileChangeHandlers.append(WeakProfileChangeHandler(handler))
    }

    static func removeProfileChangeHandler(handler: UserCenterProfileChangeProtocol) {
        if let index = profileChangeHandlers.firstIndex(where: { $0.handler === handler }) {
            profileChangeHandlers.remove(at: index)
        }
    }

    internal static func callProfileChangeHandlers() {
        profileChangeHandlers.forEach { handler in
            handler.handler?.userProfileDidChange()
        }
    }
}

public extension UserCenter {
    enum LoginType: String {
        case wechat = "Wechat"
        case apple = "Apple"
        case mobile = "Mobile"
        case unknown = ""
    }

    static var loginType: LoginType? = {
        do {
            guard let loginType: LoginTypeStorage = try store.sync.get() else { return .unknown }
            return LoginType(rawValue: loginType.value)
        } catch {
            logger.error("\(error)")
            return nil
        }
    }() {
        didSet {
            do {
                switch loginType {
                case .none, .unknown:
                    try store.sync.delete(key: LoginTypeStorage.key)
                case .some(let loginType):
                    try store.sync.put(storableObject: LoginTypeStorage(value: loginType.rawValue))
                }
            } catch {
                logger.error("\(error)")
            }
        }
    }

    static var guid: String {
        userInfo?.guid ?? ""
    }

    private(set) static var sessionID: String? = {
        do {
            guard let sessionIDStorage: SessionIDStorage = try store.sync.get() else { return nil }
            return sessionIDStorage.value
        } catch {
            logger.error("\(error)")
            return nil
        }
    }() {
        didSet {
            do {
                switch sessionID {
                case .none:
                    try store.sync.delete(key: SessionIDStorage.key)
                case .some(let sessionID):
                    try store.sync.put(storableObject: SessionIDStorage(value: sessionID))
                }
            } catch {
                logger.error("\(error)")
            }
        }
    }

    private(set) static var userInfo: UserInfo? = {
        do {
            guard let userInfo: UserInfo = try store.sync.get() else { return nil }
            return userInfo
        } catch {
            logger.error("\(error)")
            return nil
        }
    }() {
        didSet {
            callProfileChangeHandlers()
            do {
                switch userInfo {
                case .none:
                    try store.sync.delete(key: UserInfo.key)
                case .some(let userInfo):
                    try store.sync.put(storableObject: userInfo)
                }
            } catch {
                logger.error("\(error)")
            }
        }
    }
}

extension UserCenter {
    static func update(sessionID: String?) {
        self.sessionID = sessionID
    }

    static func update(userInfo: UserInfo?) {
        self.userInfo = userInfo
    }
}

public extension UserCenter {
    static var isLogin: Bool {
        !guid.isEmpty
    }
}

// MARK: - GRPC wrapper

public extension UserCenter {
    // 退出登录
    static func logout(complete: @escaping (Result<Void, Error>) -> Void) {
        RPCCenter.azir.logout { result in
            switch result {
            case .success:
                logoutAndClear()
                complete(.success(()))
            case .failure(let error):
                complete(.failure(error))
            }
        }
    }

    // 注销
    static func logoff(complete: @escaping (Result<Void, Error>) -> Void) {
        RPCCenter.azir.logoff { result in
            switch result {
            case .success:
                logoutAndClear()
                complete(.success(()))
            case .failure(let error):
                complete(.failure(error))
            }
        }
    }
}

private extension UserCenter {
    static func clearStorage() {
        userInfo = nil
        sessionID = nil
        loginType = .unknown
        do {
            try store.sync.empty()
        } catch {
            logger.error("\(error)")
        }
        logger.info("storage cleared")
    }
}

public extension UserCenter {
    static func forceLogout() {
        logoutAndClear()
        DispatchQueue.main.async {
            enterAppCallback?()
        }
    }
}
